From 8bc5b96a556cba96d92cff83cfc490e3198934a1 Mon Sep 17 00:00:00 2001 From: Carl Lerche Date: Mon, 10 Mar 2014 14:47:10 -0700 Subject: [PATCH] Move design documents --- DESIGN/DESIGN.md | 152 +++++++++++++++++++++++++++++++++++++++++++++ DESIGN/MANIFEST.md | 142 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 294 insertions(+) create mode 100644 DESIGN/DESIGN.md create mode 100644 DESIGN/MANIFEST.md diff --git a/DESIGN/DESIGN.md b/DESIGN/DESIGN.md new file mode 100644 index 000000000..d91d25c78 --- /dev/null +++ b/DESIGN/DESIGN.md @@ -0,0 +1,152 @@ +## Subcommands + +The top-level `cargo` command delegates to sub-commands named +`cargo-foo`. + +``` +$ cargo compile +# delegates to cargo-compile +``` + +By default, Cargo will come with a set of built-in commands that don't +need to be on the `$PATH`, but additional commands can be added to the +`$PATH`. + +There will also be an additional configuration for locating Cargo +subcommands that are not on the `$PATH`. + +### Input/Output + +By default, Cargo subcommands are built by implementing the +`CargoCommand` trait. This trait will pass structured data to the +command. + +By default, commands will communicate with each other via JSON data, and +the `CargoCommand` trait will convert the JSON data into the structured +data needed by the command. All commands must implement JSON data +output. + +Commands must also implement human-readable output, and may implement +additional output forms (such as tab- or space-separated output) for use +in other scripting languages. + +```rs +// The main entry point for new commands to implement +trait CargoCommand { + fn execute(input: T, logger: L) -> Result; +} + +// For now, the raw IPC communication is represented as JSON primitive +// values. The ConvertToRaw trait below converts a string protocol into the +// Raw format. Obviously, the JSON string protocol can trivially be +// converted, but other line protocols need to be defined on a +// case-by-case basis. +type Raw = serialize::json::Json; +type Flags = Map<~str, serialize::json::Json> + +// This is a list of available IPC String protocols. To start, we'll +// support JSON and an (optional) arbitrary type-defined line protocol. +enum Input { + JSONString(~str), + LineOrientedString(~str) +} + +// This trait supports converting any supported input form into Raw. +trait ConvertToRaw { + fn convert(input: Input) -> Raw; +} + +// This is the runner implementation. It will not need to be implemented +// by individual commands. +fn execute_command(command: C, config: Config, logger: L) -> Output { + match command.execute(input, logger) { + Ok(success) => { + // serialize success + }, + Err(failure) => { + // error handling/output + } + } +} + +// This is an example configuration. It is the combination of the Raw +// input from a previous command and any flags passed to this command. +// Top-level commands will mostly be configured via flags -- plumbing +// commands will be mostly configured via Raw. +// +// Note that because configurations serve as both input and output, and +// the ConvertToRaw trait handles both sides of the pipe, these definitions +// are not part of an individual command. Some configuration structures +// may even be used by multiple different commands. +struct CompileConfig { + flags: ~[~str], + path: ~[~str], + lib_path: ~str +} + +struct CompileConfigBuilder { + flags: Option<~[~str]>, + path: Option<~[~str]>, + lib_path: Option<~str> +} + +// For now, configurations manually convert the Flags and Raw into a +// single configuration object. This is the main point where a failure +// can occur that is not type-checked. All other functions receive the +// structured type and will get compiler help. +impl CompileConfig { + pub fn deserialize(flags: Flags, raw: Raw) -> CompileConfig { + CompileConfig{ flags: raw.at("flags"), path: raw.at("path"), lib_path: flags.at("lib_path") } + } +} + +// Configurations must implement ConvertIntoRaw and may +// implement other ConvertIntoRaw converters. +impl ConvertToRaw for CompileConfig { + fn convert(input: JSONString) -> Raw { + + } +} + +impl ConvertToRaw for CompileConfig { + fn convert(input: LineOrientedString) -> Raw { + + } +} + +impl ConvertFlags for CompileConfig { + fn convert(input: FlagDefinition) -> Flags { + + } +} + +// Commands are simple objects that implement CargoCommand for a given +struct CompileCommand; + +impl CompileCommand { + fn new() -> CompileCommand { CompileCommand } +} + +impl CargoCommand for CompileCommand { + fn execute(input: CompileConfig, logger: L) -> Result; + + } +} + +fn main() { + let args = parse_arguments(f); + let config = process_args_and_stdin(args); // { "flags": [ ... ] } + let command = CompileCommand::new() + let logger = CargoLogger::for(config); + let result = execute_command(command, config, logger); + + // deal with serialized output or error +} + +fn process_args_and_stdin(args: Flags) -> CompileConfig { + // delegate to other generic function; Flags tells us which serializer + // to use +} +``` + +## Configuration diff --git a/DESIGN/MANIFEST.md b/DESIGN/MANIFEST.md new file mode 100644 index 000000000..194ccff5a --- /dev/null +++ b/DESIGN/MANIFEST.md @@ -0,0 +1,142 @@ +The manifest file (`Cargo.toml`) is still under active development. This document captures the current thinking on the design. + +## Sections + +The `Cargo.toml` file contains several top-level sections: + +* `[project]`: project-specific details, such as name, version and author +* `[[lib]]`: information about the main library file, if one exists. By + default, the main library file is `src/.rs` +* `[[executable]]`: optionally repeated information about executables + that the project is generating. This can both be used for projects + that primarily build executables, as well as projects that contain + utility executables (such as an HTTP library that comes with a web + server executable). + +## Extensibility + +In general, any unknown attributes are ignored for future extensibility. +Future plugins may also use this capability. + +The project uses the `Decoder` in `rust-toml` to decode the manifest +into Rust structs that are used throughout the built-in commands. + +## The `[project]` Section + +* `name`: the name of the project (`~str`) +* `version`: the version of the project, (`~str` that can be parsed + via `semver::parse`) +* `readme`: a Markdown-formatted file in the project that can be used as + a description of the document in indexes (`Option`, relative to + the project root, defaults to "./README.md", if found). +* `tags`: an array of tags that can be used in indexes (`~[~str]`) +* `authors`: a list of authors in `name ` format (`~[~str]`). At + least one `author` with email will probably be required to submit to + the Cargo repository. +* `src`: the root directory containing source files (`Option`, + relative to the project root, defaults to `src`) + +## The `[[lib]]` Section + +At the moment, Cargo only supports a single lib per package. We use the +array format for future extensibility. + +We only plan to support a single lib at the moment because if you have +multiple libs, you would want projects to be able to depend on them +separately. If you don't care about that, why do you have separate libs? + +* `name`: the name of the library (`~str`, `hammer` would create a `libhammer`) +* `path`: the location of the main crate file (`Option`, defaults to + `src/.rs`) + +Note that we plan to support multiple `Cargo.toml` files in Cargo's git +support, so you don't have to have a separate git repository per +library. + +## The `[[executable]]` Section + +The `executable` section is optionally repeated. It is designed for +projects whose main raison d'être is a single executable, or for projects +that want to provide utility executables alongside a primary library. + +If an executable has a different set of flags or dependencies from the +main library, it should be shipped as a separate package with a +dependency on the main library to keep the usage requirements of the +standalone library limited to the bare minimum requirements. + +* `name`: the name of the executable (`~str`, `hammer` would create a + `hammer` executable) +* `path`: the location of the main crate file for the executable + (`Option`, defaults to `src/.rs` if the project has only + an executable, `src/bin/.rs` if the project has both a lib and + executable, see below) + +## Projects Containing Both `lib` and `executable` + +Most projects will primarily produce either a library or an executable. +Such projects should name the main crate file `.rs` and +omit the optional `path` from the `lib` or `executable` sections. + +Projects that contain both a library and one or more executables should +generally use `.rs` for their library, and `bin/*.rs` +for the executables. + +These rules are also the default behavior if `path` is left off of `lib` +or `executable` sections. + +## Example Manifests + +Simple `lib`: + +```toml +[project] + +name = "hammer" +version = "0.1.0" +readme = "README.md" +authors = ["Yehuda Katz "] + +[[lib]] + +name = "hammer" +``` + +Simple `executable`: + +```toml +[project] + +name = "skylight" +version = "0.5.0" +authors = ["Tom Dale ", "Carl Lerche "] +tags = ["performance", "profiling"] + +[[executable]] + +name = "skylight" +path = "bin/skylight.rs" # supports existing project structures +``` + +A project with both a lib and an `executable`: + +```toml +[project] + +name = "skylight" +version = "0.5.0" +authors = ["Tom Dale ", "Carl Lerche "] +tags = ["performance", "profiling"] + +[[lib]] + +name = "skylight" # path defaults to src/skylight.rs + +[[executable]] + +name = "skylight" # path defaults to src/bin/skylight.rs + +[[executable]] + +name = "skylight-setup" # path defaults to src/bin/skylight-setup.rs +``` + -- 2.30.2